目录
- PLSQL的基本概念
- PLSQL的编写
- 实例1:编写输出语句
- 实例2:定义变量并动态赋值变量
- PLSQL的变量推荐设置
- 实例3:引用型变量与记录型变量
- PLSQL的选择结构:if语句
- 实例4:选择结构
- PLSQL的循环结构
- 实例5:循环结构
- 游标的基本概念
- 游标的创建
- 游标的四个属性
- 实例6:游标的基本使用
- 游标的例外(异常)
- 实例7:游标的例外
- 实例8:游标的使用场景
PLSQL的基本概念
可以对SQL进行编程,比如:if...else...
、for
循环、存储过程、存储函数等。
开发工具
- PL/SQL Developer
- Oracle SQL Developer
PLSQL的编写
通用模板
1 | declare |
实例1:编写输出语句
编写输出语句“hello world”
1 | set serverout on; |
输出结果:
1 | hello world |
注意:
要打开输出语句的设置,默认为关闭。
1 | set serverout on; |
实例2:定义变量并动态赋值变量
1 | declare |
输出结果:
1 | hello world:男 |
但此例是我们不常用并且是不推荐使用的,由此引出在PLSQL中对变量的推荐设置。
PLSQL的变量推荐设置
PLSQL的变量分为两类:
- 引用型变量。比如:
pename emp.ename%type;
即变量pename
与emp
表中的ename
字段的类型保持一致,此类型包括字段类型和字段长度等。以便于之后如果emp
表的字段发生改变而导致PLSQL无法使用。 - 记录型变量。比如:
emp_info emp%rowtype;
即变量emp_info
与emp
表中的所有行的类型都保持一致。类似于Java的对象:Person per = new Person(1, 'zs', 28, 175cm, 65kg);
,可以用于同时保存多个变量值。
实例3:引用型变量与记录型变量
1 | declare |
输出结果:
1 | 7499---ALLEN---SALESMAN |
PLSQL的选择结构:if语句
有三种语法:
第一种
1
2if 条件 then ... ;
end if;第二种
1
2
3if 条件 then ...;
else ...
end if;第三种
1
2
3
4if 条件 then ...;
elsif 条件 then ...;
else ...;
end if ;
实例4:选择结构
pnum
为 1 时:
1 | declare |
输出结果:
1 | 一 |
pnum
为 3 时:
1 | declare |
输出结果:
1 | 其他 |
PLSQL的循环结构
for
语句1
2
3
4for i in 开始 .. 结束
loop
...
end loop;while
语句1
2
3
4while 条件
loop
...
end loop;do while
语句1
2
3
4loop
...
exit when 条件;
end loop;
注意:
PLSQL的do while
语句与Java的do while
语句的判断条件是相反的!
比如 i大于5是结束。PLSQL表示为exit when i > 5;
而Java表示为while(i <= 5)
。
实例5:循环结构
for
语句
输出数字1—5。1
2
3
4
5
6
7
8declare
begin
for i in 1 .. 5
loop
dbms_output.put_line(i);
end loop;
end;
输出结果:
1
2
3
4
5
6
7
1
2
3
4
5
PL/SQL 过程已成功完成。
do while
语句
计算1—5之和。1
2
3
4
5
6
7
8
9
10
11declare
psum number := 0;
i number := 1;
begin
loop
psum := psum + i;
i := i + 1;
exit when i > 5;
end loop;
dbms_output.put_line(psum);
end;
输出结果:
1
2
3
15
PL/SQL 过程已成功完成。
游标(光标)
游标的基本概念
比如:select ename from emp;
,输出结果为:
1 | ENAME |
现在ename
有一堆数据,怎么保存呢?之前的变量pname
只能保存一个或一行数据,而多行数据或一大推数据该怎么保存呢?由此引出:
游标(光标)cursor
:用来保存一个集合的数据。
游标的创建
语法:
1 | cursor 游标名(参数列表) |
游标的四个属性
%isopen:
游标使用之前必须打开。用来判断游标是否开启。%rowcount:
用来判断游标已经读取了多少行数据。%found:
判断当前行数据的下一行数据是否存在。%notfound:
判断当前行数据的下一行数据是否存在。
实例6:游标的基本使用
查询并打印全体员工的姓名、薪水,如图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14set serveroutput on;
declare
cursor cemp is select ename, sal from emp;
pename emp.ename%type;
psal emp.sal%type;
begin
open cemp;--1.打开游标。
loop --2.循环 准备获取每一行数据。
fetch cemp into pename, psal; --3.一行一行获取游标的值。
exit when cemp%notfound;
dbms_output.put_line(pename || '工资是:' || psal);
end loop;
close cemp;--关闭游标。
end;
输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
xxx工资是:999
xxx工资是:999
x工资是:9999
x工资是:9999
李四工资是:
SMITH工资是:800
ALLEN工资是:1600
WARD工资是:1250
JONES工资是:2975
MARTIN工资是:1250
BLAKE工资是:2850
CLARK工资是:2450
SCOTT工资是:3000
KING工资是:5000
TURNER工资是:1500
ADAMS工资是:1100
JAMES工资是:950
FORD工资是:3000
MILLER工资是:1300
seq工资是:
PL/SQL 过程已成功完成。
> 注意:
> 游标有几个字段,就定义几个变量进行保存。
> 一行一行的取数据,必然需要使用循环。
按照职位进行涨工资,PRESIDENT职位涨薪1000元,MANAGER职位涨薪800元,其他职位涨薪400元。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22set serveroutput on;
declare
cursor cemp is select empno, job from emp;
pempno emp.empno%type;
pjob emp.job%type;
begin
open cemp;--打开游标。
loop
fetch cemp into pempno, pjob;
exit when cemp%notfound;
--判断职位
if pjob = 'PRESIDENT'
then update emp set sal = sal + 1000 where empno = pempno;
elsif pjob = 'MANAGER'
then update emp set sal = sal + 800 where empno = pempno;
else
update emp set sal = sal + 400 where empno = pempno;
end if;
end loop;
close cemp;--关闭游标。
end;
commit;
> 注意:
> Oralce 的ACID默认级别是:`read commit`即一边(终端)不提交,另一个访问不到。
查询某个部门的员工姓名,带参数的光标。
1
2
3
4
5
6
7
8
9
10
11
12declare
cursor cename(dno number) is select ename from emp where deptno = dno;
pename emp.ename%type;
begin
open cename(20);
loop
fetch cename into pename;
exit when cename%notfound;
dbms_output.put_line(pename);
end loop;
close cename;
end;
输出结果:
1
2
3
4
5
6
SMITH
ADAMS
SCOTT
JONES
PL/SQL 过程已成功完成。
游标的例外(异常)
系统例外:
no_data_found
:没有该数据。too_many_rows
:变量无法保存太多的数据。zero_divide
:数学例外。value_error
:算术或转换错误。timeout_on_resource
:资源等待超时。(分布式数据库)
自定义例外:
就像Java一样,自建一个异常,如果满足条件则抛出异常,然后用
try...catch
进行捕获。比如:1
2
3
4
5
6
7
自建异常:MyExc
try{
if(...) throws new MyExc();
}catch(){...}
catch(){...}
实例7:游标的例外
系统例外
1 | declare |
输出结果:
1 | 0不能作为除数 |
自定义例外
1 | declare |
输出结果:
1 | 自定义例外 |
是否存在问题
是否存在编号为50的部门,如果不存在,抛出一个例外;如果存在,将该部门的员工姓名打印出来。
1 | set serveroutput on; |
输出结果:
1 | 自定义例外,没有此号部门。 |
实例8:游标的使用场景
统计每年的入职人数
思考普通SQL的做法:
1
2
3
4
5
6
7
8SQL> select to_char(hiredate, 'yyyy'), count(*) from emp group by to_char(hiredate, 'yyyy');
TO_C COUNT(*)
---- ----------
1987 2
1980 1
1982 1
1981 10思考Java的做法:
将每个人的数据获取出来,判断入职年份,如果是1980年,则计数+1;如果是1981年,则计数+1,以此类推。
将Java思想转为PLSQL的做法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26set serveroutput on;
declare
cursor cemp is select to_char(hiredate, 'yyyy') from emp;
phiredate varchar(4);--判断年份,以备计数。
count80 number := 0;
count81 number := 0;
count82 number := 0;
count87 number := 0;
begin
open cemp;
loop
fetch cemp into phiredate;
exit when cemp%notfound;
if phiredate = '1980' then count80 := count80 + 1;
elsif phiredate = '1981' then count81 := count81 + 1;
elsif phiredate = '1982' then count82 := count82 + 1;
else count87 := count87 + 1;
end if;
end loop;
dbms_output.put_line('1980 ' || count80);
dbms_output.put_line('1981 ' || count81);
dbms_output.put_line('1982 ' || count82);
dbms_output.put_line('1987 ' || count87);
dbms_output.put_line('总人数 ' || ( count80 + count81 + count82 + count87 ) );
close cemp;
end;输出结果:
1
2
3
4
5
6
71980 1
1981 10
1982 1
1987 8
总人数 20
PL/SQL 过程已成功完成。
涨工资问题
每人涨10%的工资,但是按入职时间顺序涨工资,且涨后的全体员工的总工资不能超过5万,超过则剩下的员工就不涨工资了。计算需要涨工资的人的个数以及涨后的工资总额。
1 | set serveroutput on; |
输出结果:
1 | 人数14--- 涨后的总额40067.5 |
统计各部门的工资情况
格式如下
1 | 部门编号 工资小于2000的人数 工资在2000至4000的人数 工资大于4000的人数 各部门工资总额 |
输出结果:
1 | set serveroutput on; |